<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="lUAqs" id="lUAqs"><span data-lake-id="u8a297e8b" id="u8a297e8b">典型回答</span></h1>
  <p data-lake-id="u61c1ada9" id="u61c1ada9"><span data-lake-id="u1ae97b6f" id="u1ae97b6f">​</span><br></p>
  <p data-lake-id="u9f4531ac" id="u9f4531ac"><span data-lake-id="u99b4c175" id="u99b4c175">在Java生态体系中，围绕着日志，有很多成熟的解决方案。关于日志输出，主要有两类工具。</span></p>
  <p data-lake-id="ua03b9409" id="ua03b9409"><br></p>
  <p data-lake-id="u8e117d08" id="u8e117d08"><span data-lake-id="u0c2d8dcc" id="u0c2d8dcc">一类是日志框架（Log4j、Logback），主要用来进行日志的输出的，比如输出到哪个文件，日志格式如何等。 另外一类是日志门面（slf4j，commons-logging），主要一套通用的API，用来屏蔽各个日志框架之间的差异的。</span></p>
  <p data-lake-id="u0fa836b1" id="u0fa836b1"><br></p>
  <p data-lake-id="ufe6ca6f4" id="ufe6ca6f4"><span data-lake-id="u5b010859" id="u5b010859">对于Java工程师来说，关于日志工具的使用，最佳实践就是在应用中使用如Log4j + SLF4J 这样的组合来进行日志输出。</span></p>
  <p data-lake-id="u4a1eff25" id="u4a1eff25"><br></p>
  <p data-lake-id="ub64d0f52" id="ub64d0f52"><strong><span data-lake-id="uda690e7c" id="uda690e7c">这样做的最大好处，就是业务层的开发不需要关心底层日志框架的实现及细节，在编码的时候也不需要考虑日后更换框架所带来的成本。这也是门面模式所带来的好处。</span></strong></p>
  <p data-lake-id="u3e6a1707" id="u3e6a1707"><br></p>
  <h1 data-lake-id="eXm5L" id="eXm5L"><span data-lake-id="ucd3d6e37" id="ucd3d6e37">扩展知识</span></h1>
  <h2 data-lake-id="mXkWQ" id="mXkWQ"><span data-lake-id="ub73b67b3" id="ub73b67b3">常用日志框架</span></h2>
  <p data-lake-id="u3da4a364" id="u3da4a364"><br></p>
  <h3 data-lake-id="mhU8s" id="mhU8s"><span data-lake-id="u6e96ff9d" id="u6e96ff9d">j.u.l</span></h3>
  <p data-lake-id="u3e770351" id="u3e770351"><br></p>
  <p data-lake-id="u07e4e5d0" id="u07e4e5d0"><span data-lake-id="u62abcec5" id="u62abcec5">j.u.l是java.util.logging包的简称，是JDK在1.4版本中引入的Java原生日志框架。Java Logging API提供了七个日志级别用来控制输出。这七个级别分别是：SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。</span></p>
  <p data-lake-id="u12ce1834" id="u12ce1834"><br></p>
  <h3 data-lake-id="ZJPjG" id="ZJPjG"><span data-lake-id="u913a2b77" id="u913a2b77">Log4j</span></h3>
  <p data-lake-id="u8903eae0" id="u8903eae0"><br></p>
  <p data-lake-id="u6d1089f9" id="u6d1089f9"><br></p>
  <p data-lake-id="u2d6ac06e" id="u2d6ac06e"><span data-lake-id="uab00e7e4" id="uab00e7e4">Log4j是Apache的一个开源项目，通过使用Log4j，我们可以控制日志信息输送的目的地是控制台、文件、GUI组件，甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等；我们也可以控制每一条日志的输出格式；通过定义每一条日志信息的级别，我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是，这些可以通过一个配置文件来灵活地进行配置，而不需要修改应用的代码。</span></p>
  <p data-lake-id="udf237d49" id="udf237d49"><br></p>
  <p data-lake-id="uc42c9b39" id="uc42c9b39"><span data-lake-id="u054a7c79" id="u054a7c79">Log4也有七种日志级别：OFF、FATAL、ERROR、WARN、INFO、DEBUG和TRACE。</span></p>
  <p data-lake-id="ud6215cae" id="ud6215cae"><br></p>
  <h3 data-lake-id="TosZn" id="TosZn"><span data-lake-id="u8f78d532" id="u8f78d532">LogBack</span></h3>
  <p data-lake-id="u11ccc31f" id="u11ccc31f"><br></p>
  <p data-lake-id="ub8ce7f9a" id="ub8ce7f9a"><span data-lake-id="u2ecf98cd" id="u2ecf98cd">LogBack也是一个很成熟的日志框架，其实LogBack和Log4j出自一个人之手，这个人就是Ceki Gülcü。</span></p>
  <p data-lake-id="ua98bc47a" id="ua98bc47a"><br></p>
  <p data-lake-id="ubaa6865a" id="ubaa6865a"><span data-lake-id="u49825aaa" id="u49825aaa">logback当前分成三个模块：logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是Log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如Log4j或j.u.l。logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能。</span></p>
  <p data-lake-id="ufa0d0263" id="ufa0d0263"><br></p>
  <h3 data-lake-id="bKERX" id="bKERX"><span data-lake-id="u98e306bd" id="u98e306bd">Log4j2 </span></h3>
  <p data-lake-id="u2f105b20" id="u2f105b20"><br></p>
  <p data-lake-id="u7ee21a97" id="u7ee21a97"><span data-lake-id="u36ed7e0f" id="u36ed7e0f">前面介绍过Log4j，这里要单独介绍一下Log4j2，之所以要单独拿出来说，而没有和Log4j放在一起介绍，是因为作者认为，Log4j2已经不仅仅是Log4j的一个升级版本了，而是从头到尾被重写的，这可以认为这其实就是完全不同的两个框架。</span></p>
  <p data-lake-id="u80ad1315" id="u80ad1315"><br></p>
  <p data-lake-id="u2ce0f260" id="u2ce0f260"><span data-lake-id="uf89fde4e" id="uf89fde4e">关于Log4j2解决了Log4j的哪些问题，Log4j2相比较于Log4j、j.u.l和logback有哪些优势，我们在后续的文章中介绍。</span></p>
  <p data-lake-id="u2b622790" id="u2b622790"><br></p>
  <p data-lake-id="uec5d3c23" id="uec5d3c23"><span data-lake-id="ucca4c865" id="ucca4c865">前面介绍了四种日志框架，也就是说，我们想要在应用中打印日志的时候，可以使用以上四种类库中的任意一种。比如想要使用Log4j，那么只要依赖Log4j的jar包，配置好配置文件并且在代码中使用其API打印日志就可以了。</span></p>
  <p data-lake-id="u6b0c061c" id="u6b0c061c"><br></p>
  <p data-lake-id="ub3e84816" id="ub3e84816"><span data-lake-id="u1d25911e" id="u1d25911e">不知道有多少人看过《阿里巴巴Java开发手册》，其中有一条规范做了『强制』要求：</span></p>
  <p data-lake-id="u6aa1d46a" id="u6aa1d46a"><br></p>
  <p data-lake-id="u593137cd" id="u593137cd"><img src="http://www.hollischuang.com/wp-content/uploads/2018/11/15430457243811.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_53%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="ua7ac6a71" id="ua7ac6a71">​</span></p>
  <p data-lake-id="ueecc3a68" id="ueecc3a68"><br></p>
  <p data-lake-id="uef23a825" id="uef23a825"><span data-lake-id="ub11f03b4" id="ub11f03b4">说好了以上四种常用的日志框架是给Java应用提供的方便进行记录日志的，那为什么又不让在应用中直接使用其API呢？这里面推崇使用的SLF4J是什么呢？所谓的门面模式又是什么东西呢？</span></p>
  <p data-lake-id="u97285911" id="u97285911"><br></p>
  <h2 data-lake-id="DWLbf" id="DWLbf"><span data-lake-id="u2c9dca13" id="u2c9dca13">什么是日志门面</span></h2>
  <p data-lake-id="uaf04772a" id="uaf04772a"><br></p>
  <p data-lake-id="u27183e02" id="u27183e02"><span data-lake-id="u6ac1d2dc" id="u6ac1d2dc">日志门面，是门面模式的一个典型的应用。</span></p>
  <p data-lake-id="u01e7be0e" id="u01e7be0e"><br></p>
  <p data-lake-id="ueb8cb183" id="ueb8cb183"><span data-lake-id="u8903b1da" id="u8903b1da">门面模式（Facade Pattern），也称之为外观模式，其核心为：外部与一个子系统的通信必须通过一个统一的外观对象进行，使得子系统更易于使用。</span></p>
  <p data-lake-id="uce32df97" id="uce32df97"><br></p>
  <p data-lake-id="ua0d9e3a9" id="ua0d9e3a9"><img src="http://www.hollischuang.com/wp-content/uploads/2018/11/15430470508468.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_16%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="ue619a167" id="ue619a167">​</span></p>
  <p data-lake-id="uca875520" id="uca875520"><br></p>
  <p data-lake-id="uc1b68523" id="uc1b68523"><span data-lake-id="u186feadb" id="u186feadb">就像前面介绍的几种日志框架一样，每一种日志框架都有自己单独的API，要使用对应的框架就要使用其对应的API，这就大大的增加应用程序代码对于日志框架的耦合性。</span></p>
  <p data-lake-id="u909d8345" id="u909d8345"><br></p>
  <p data-lake-id="uf9e95c94" id="uf9e95c94"><span data-lake-id="ubb0b5e16" id="ubb0b5e16">为了解决这个问题，就是在日志框架和应用程序之间架设一个沟通的桥梁，对于应用程序来说，无论底层的日志框架如何变，都不需要有任何感知。只要门面服务做的足够好，随意换另外一个日志框架，应用程序不需要修改任意一行代码，就可以直接上线。</span></p>
  <p data-lake-id="u381abfde" id="u381abfde"><br></p>
  <p data-lake-id="u8b595742" id="u8b595742"><span data-lake-id="u95c64620" id="u95c64620">在软件开发领域有这样一句话：计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。而门面模式就是对于这句话的典型实践。</span></p>
  <p data-lake-id="u74148d10" id="u74148d10"><br></p>
  <h2 data-lake-id="ZvXj3" id="ZvXj3"><span data-lake-id="u4b4d4f9f" id="u4b4d4f9f">为什么需要日志门面</span></h2>
  <p data-lake-id="u99d61b20" id="u99d61b20"><br></p>
  <p data-lake-id="u4e60fb0c" id="u4e60fb0c"><span data-lake-id="uba767b07" id="uba767b07">前面提到过一个重要的原因，就是为了在应用中屏蔽掉底层日志框架的具体实现。这样的话，即使有一天要更换代码的日志框架，只需要修改jar包，最多再改改日志输出相关的配置文件就可以了。这就是解除了应用和日志框架之间的耦合。</span></p>
  <p data-lake-id="u7c2dcfb7" id="u7c2dcfb7"><br></p>
  <p data-lake-id="u20f0052a" id="u20f0052a"><span data-lake-id="uac2ecec9" id="uac2ecec9">有人或许会问了，如果我换了日志框架了，应用是不需要改了，那日志门面不还是需要改的吗？</span></p>
  <p data-lake-id="ucfbe9e12" id="ucfbe9e12"><br></p>
  <p data-lake-id="uf4d4d014" id="uf4d4d014"><span data-lake-id="u7f6f8387" id="u7f6f8387">要回答这个问题，我们先来举一个例子，再把门面模式揉碎了重新解释一遍。</span></p>
  <p data-lake-id="uba28e805" id="uba28e805"><br></p>
  <p data-lake-id="udfa7b794" id="udfa7b794"><span data-lake-id="uf01686f6" id="uf01686f6">日志门面就像饭店的服务员，而日志框架就像是后厨的厨师。对于顾客这个应用来说，我到饭店点菜，我只需要告诉服务员我要一盘番茄炒蛋即可，我不关心后厨的所有事情。因为虽然主厨从把这道菜称之为『番茄炒蛋』A厨师换成了把这道菜称之为『西红柿炒鸡蛋』的B厨师。但是，顾客不需要关心，他只要下达『番茄炒蛋』的命令给到服务员，由服务员再去翻译给厨师就可以了。</span></p>
  <p data-lake-id="u64a7e462" id="u64a7e462"><br></p>
  <p data-lake-id="u466c0f6e" id="u466c0f6e"><span data-lake-id="ud6fd13a0" id="ud6fd13a0">所以，对于一个了解了"番茄炒蛋的多种叫法"的服务员来说，无论后厨如何换厨师，他都能准确的帮用户下单。</span></p>
  <p data-lake-id="u47ba40ff" id="u47ba40ff"><br></p>
  <p data-lake-id="ueb590ad9" id="ueb590ad9"><span data-lake-id="uef4bf8a9" id="uef4bf8a9">同理，对于一个设计的全面、完善的日志门面来说，他也应该是天然就兼容了多种日志框架的。所以，底层框架的更换，日志门面几乎不需要改动。</span></p>
  <p data-lake-id="u1a26f675" id="u1a26f675"><br></p>
  <p data-lake-id="ua025c386" id="ua025c386"><span data-lake-id="ud599d83c" id="ud599d83c">以上，就是日志门面的一个比较重要的好处——解耦。</span></p>
  <p data-lake-id="ud13ef9b7" id="ud13ef9b7"><br></p>
  <h2 data-lake-id="Bgj9M" id="Bgj9M"><span data-lake-id="u9f1c9e38" id="u9f1c9e38">常用日志门面</span></h2>
  <p data-lake-id="u9d3c4cfa" id="u9d3c4cfa"><br></p>
  <p data-lake-id="u30e0efec" id="u30e0efec"><span data-lake-id="uab636fee" id="uab636fee">介绍过了日志门面的概念和好处之后，我们看看Java生态体系中有哪些好的日志门面的实现可供选择。</span></p>
  <p data-lake-id="u587710ce" id="u587710ce"><br></p>
  <h3 data-lake-id="l81Nn" id="l81Nn"><span data-lake-id="u9556cf5d" id="u9556cf5d">SLF4J</span></h3>
  <p data-lake-id="u956043a9" id="u956043a9"><br></p>
  <p data-lake-id="u079a81b8" id="u079a81b8"><span data-lake-id="u7d923424" id="u7d923424">Java简易日志门面（Simple Logging Facade for Java，缩写SLF4J），是一套包装Logging 框架的界面程式，以外观模式实现。可以在软件部署的时候决定要使用的 Logging 框架，目前主要支援的有Java Logging API、Log4j及logback等框架。以MIT 授权方式发布。</span></p>
  <p data-lake-id="ucc898285" id="ucc898285"><br></p>
  <p data-lake-id="uba8c0f67" id="uba8c0f67"><span data-lake-id="uca73e58c" id="uca73e58c">SLF4J 的作者就是 Log4j 的作者 Ceki Gülcü，他宣称 SLF4J 比 Log4j 更有效率，而且比 Apache Commons Logging (JCL) 简单、稳定。</span></p>
  <p data-lake-id="uc88c5f50" id="uc88c5f50"><br></p>
  <p data-lake-id="uaefc5957" id="uaefc5957"><span data-lake-id="uf60c6f0d" id="uf60c6f0d">其实，SLF4J其实只是一个门面服务而已，他并不是真正的日志框架，真正的日志的输出相关的实现还是要依赖Log4j、logback等日志框架的。</span></p>
  <p data-lake-id="u740224d4" id="u740224d4"><br></p>
  <p data-lake-id="ubb0b5a1f" id="ubb0b5a1f"><span data-lake-id="u6e883c4b" id="u6e883c4b">由于SLF4J比较常用，这里多用一些篇幅，再来简单分析一下SLF4J，主要和Log4J做一下对比。相比较于Log4J的API，SLF4J有以下几点优势：</span></p>
  <p data-lake-id="uad0f607c" id="uad0f607c"><br></p>
  <ul list="u503a2c39">
   <li fid="u78c63816" data-lake-id="ub88d09a0" id="ub88d09a0"><span data-lake-id="u899fe932" id="u899fe932"> Log4j 提供 TRACE, DEBUG, INFO, WARN, ERROR 及 FATAL 六种纪录等级，但是 SLF4J 认为 ERROR 与 FATAL 并没有实质上的差别，所以拿掉了 FATAL 等级，只剩下其他五种。 </span></li>
   <li fid="u78c63816" data-lake-id="u2e464266" id="u2e464266"><span data-lake-id="u062c36bd" id="u062c36bd"> 大部分人在程序里面会去写logger.error(exception),其实这个时候Log4j会去把这个exception tostring。真正的写法应该是logger(message.exception);而SLF4J就不会使得程序员犯这个错误。 </span></li>
   <li fid="u78c63816" data-lake-id="u67faa9a0" id="u67faa9a0"><span data-lake-id="u74723b64" id="u74723b64"> Log4j间接的在鼓励程序员使用string相加的写法（这种写法是有性能问题的），而SLF4J就不会有这个问题 ,你可以使用logger.error("{} is+serviceid",serviceid); </span></li>
   <li fid="u78c63816" data-lake-id="ub7b76ca6" id="ub7b76ca6"><span data-lake-id="u7f8c3648" id="u7f8c3648"> 使用SLF4J可以方便的使用其提供的各种集体的实现的jar。（类似commons-logger） </span></li>
   <li fid="u78c63816" data-lake-id="u56c9cd79" id="u56c9cd79"><span data-lake-id="u3ba16d2d" id="u3ba16d2d"> 从commons--logger和Log4j merge非常方便，SLF4J也提供了一个swing的tools来帮助大家完成这个merge。 </span></li>
   <li fid="u78c63816" data-lake-id="u15774a8c" id="u15774a8c"><span data-lake-id="u2c8929be" id="u2c8929be"> 提供字串内容替换的功能，会比较有效率，说明如下： </span></li>
  </ul>
  <pre lang="java"><code>
// 传统的字符串产生方式，如果没有要记录Debug等级的信息，就会浪费时间在产生不必要的信息上
logger.debug("There are now " + count + " user accounts: " + userAccountList);

// 为了避免上述问题，我们可以先检查是不是开启了Debug信息记录功能，只是程序的编码会比较复杂
if (logger.isDebugEnabled()) {
    logger.debug("There are now " + count + " user accounts: " + userAccountList);
}

// 如果Debug等级没有开启，则不会产生不必要的字符串，同时也能保持程序编码的简洁
logger.debug("There are now {} user accounts: {}", count, userAccountList);
</code></pre>
  <p data-lake-id="ud2e9a050" id="ud2e9a050"><span data-lake-id="u1c3ff1df" id="u1c3ff1df"> </span></p>
  <ul list="u30adb4dd">
   <li fid="u172d9178" data-lake-id="u9c601473" id="u9c601473"><span data-lake-id="u56a803ff" id="u56a803ff"> SLF4J 只支持 MDC，不支持 NDC。 </span></li>
  </ul>
  <p data-lake-id="ue8740b45" id="ue8740b45"><br></p>
  <h3 data-lake-id="ZuF6q" id="ZuF6q"><span data-lake-id="u1d115647" id="u1d115647">commons-logging</span></h3>
  <p data-lake-id="u43126f7d" id="u43126f7d"><br></p>
  <p data-lake-id="u2014bc36" id="u2014bc36"><br></p>
  <p data-lake-id="u4c4d16ed" id="u4c4d16ed"><span data-lake-id="u30c396a9" id="u30c396a9">Apache Commons Logging是一个基于Java的日志记录实用程序，是用于日志记录和其他工具包的编程模型。它通过其他一些工具提供API，日志实现和包装器实现。</span></p>
  <p data-lake-id="u6d3679c6" id="u6d3679c6"><br></p>
  <p data-lake-id="u212f82e7" id="u212f82e7"><span data-lake-id="u3bf1e78f" id="u3bf1e78f">commons-logging和SLF4J的功能是类似的，主要是用来做日志 门面的。提供更加好友的API工具。</span></p>
 </body>
</html>